/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		sdconv.cpp
 * PURPOSE:		security descriptor, ACL and ACE conversion functions
 */

namespace SDConv
{

UCHAR AclFlagsToNtAclFlags(USHORT Src)
{
	return static_cast<UCHAR>(Src & 0x00FF); //last byte is the same in both
}

void AclToNtAclAppend(IN ACL const *Src, ACL &DestHeader, auto_buffer &Buff, UCHAR (*Pred)(IN UCHAR Type) OPTIONAL = NULL)
{
	ptr_buffer<ACE> SrcAce(reinterpret_cast<ULONG_PTR>(Src) + sizeof(ACL));

	for(USHORT i = 0; i < Src->AceCount; i++)
	{
		if(!Pred || Pred(SrcAce.data->Type))
		{
			NT_ACE_HEADER Ace;
			Ace.Type = SrcAce.data->Type;
			Ace.Flags = AclFlagsToNtAclFlags(SrcAce.data->Flags);
			Ace.Size = narrowing_cast<USHORT>(max(sizeof(NT_ACE_HEADER), SrcAce.data->Size - sizeof(SDE_ACE_HEADER) + sizeof(NT_ACE_HEADER)));
			Ace.Mask = SrcAce.data->Mask;

			Buff.append(Ace); //append modified header

			Buff.append(SrcAce.data->Body, SrcAce.data->Size - sizeof(SDE_ACE_HEADER)); //append rest

			DestHeader.AceCount++;
		}
		SrcAce.pos += SrcAce.data->Size;
	}

	DestHeader.AclRevision = max(DestHeader.AclRevision, Src->AclRevision);
}

void AclToNtAclStart(auto_buffer &Buff, ACL &DestHeader, USHORT SizeHint = 0x40 OPTIONAL)
{
	Buff.increase_maxct(SizeHint); //estimate size

	RtlZeroStruct(DestHeader);
	Buff.append(DestHeader); //just a placeholder for now
}

auto_take<ACL> AclToNtAclEnd(auto_buffer &Buff, ACL &DestHeader)
{
	DestHeader.AclSize = narrowing_cast<USHORT>(Buff.get_ct());

	*Buff.get_as<ACL>() = DestHeader; //copy header bits

	return Buff.release_as<ACL>();
}

auto_take<ACL> AclToNtAcl(IN ACL const *Src, UCHAR (*Pred)(IN UCHAR Type) OPTIONAL = NULL)
{
	if(!Src)
		return auto_take<ACL>(NULL);

	auto_buffer Buff;
	ACL Header;

	AclToNtAclStart(Buff, Header, Src->AclSize);
	AclToNtAclAppend(Src, Header, Buff, Pred);
	return AclToNtAclEnd(Buff, Header);
}

void NtAclToAclStart(auto_buffer &Buff, ACL &DestHeader, USHORT SizeHint = 0x40 OPTIONAL)
{
	Buff.increase_maxct(SizeHint); //estimate size

	RtlZeroStruct(DestHeader);
	Buff.append(DestHeader); //just a placeholder for now
}

auto_take<ACL> NtAclToAclEnd(auto_buffer &Buff, ACL &DestHeader)
{
	DestHeader.AclSize = narrowing_cast<USHORT>(Buff.get_ct());

	*Buff.get_as<ACL>() = DestHeader; //copy header bits

	return Buff.release_as<ACL>();
}

void NtAclToAclAppend(IN ACL const *Src, ACL &DestHeader, auto_buffer &Buff, UCHAR (*Pred)(IN UCHAR Type) OPTIONAL = NULL)
{
	ptr_buffer<NT_ACE> SrcAce(reinterpret_cast<ULONG_PTR>(Src) + sizeof(ACL));

	for(USHORT i = 0; i < Src->AceCount; i++)
	{
		if(!Pred || Pred(SrcAce.data->Type))
		{
			SDE_ACE_HEADER Ace;
			Ace.Revision = ACE_REVISION_SDEDIT_1;
			Ace.Type = SrcAce.data->Type; //same type numbers
			Ace.Flags = SrcAce.data->Flags; //lower 8 bits are bin compat
			Ace.Size = narrowing_cast<USHORT>(max(sizeof(SDE_ACE_HEADER), SrcAce.data->Size - sizeof(NT_ACE_HEADER) + sizeof(SDE_ACE_HEADER)));
			Ace.Source = 0; //starting at 0 for unknown (other functions like SetAceSourcesAtLevel or MergeAclState may determine differently)
			Ace.Mask = SrcAce.data->Mask; //masks are bin comapt

			Buff.append(Ace); //append modified header

			Buff.append(SrcAce.data->Body, SrcAce.data->Size - sizeof(NT_ACE_HEADER)); //append rest

			DestHeader.AceCount++;
		}

		SrcAce.pos += SrcAce.data->Size;
	}
	
	DestHeader.AclRevision = max(DestHeader.AclRevision, Src->AclRevision);
}

auto_take<ACL> NtAclToAcl(IN ACL const *Src, UCHAR (*Pred)(IN UCHAR Type) OPTIONAL = NULL)
{
	if(!Src)
		return auto_take<ACL>(NULL);

	auto_buffer Buff;
	ACL Header;

	NtAclToAclStart(Buff, Header, Src->AclSize);
	NtAclToAclAppend(Src, Header, Buff, Pred);
	return NtAclToAclEnd(Buff, Header);
}

auto_take<SECURITY_DESCRIPTOR_RELATIVE> EsToNtSd(IN const SD_EDIT_STATE &Es, OUT SECURITY_INFORMATION &Si)
{
	auto_buffer Ret;
	SECURITY_DESCRIPTOR_RELATIVE Sd;
	Si = ModifiedSdEditStateSecInfo(Es);

	RtlZeroStruct(Sd);

	Ret.append(Sd);

	Sd.Revision = SECURITY_DESCRIPTOR_REVISION1;

	if(Si & OWNER_SECURITY_INFORMATION)
	{
		if(Es.Owner.Sid)
		{
			Sd.Owner = narrowing_cast<ULONG>(Ret.get_ct());
			Ret.append_szpod(Es.Owner.Sid);
		}

		if(Es.Owner.State & SID_FLAG_DEFAULTED)
			Sd.Control |= SE_OWNER_DEFAULTED;
	}

	if(Si & GROUP_SECURITY_INFORMATION)
	{
		if(Es.Group.Sid)
		{
			Sd.Group = narrowing_cast<ULONG>(Ret.get_ct());
			Ret.append_szpod(Es.Group.Sid);
		}

		if(Es.Group.State & SID_FLAG_DEFAULTED)
			Sd.Control |= SE_GROUP_DEFAULTED;
	}

	if(Si & DACL_SECURITY_INFORMATION)
	{
		if(Es.Dacl.Acl)
		{
			Sd.Dacl = narrowing_cast<ULONG>(Ret.get_ct());
			Sd.Control |= SE_DACL_PRESENT;

			auto_szpod<ACL> Acl = AclToNtAcl(Es.Dacl.Acl);
			Ret.append_szpod(Acl);
		}

		if(Es.Dacl.State & ACL_FLAG_DEFAULTED)
			Sd.Control |= SE_DACL_DEFAULTED;
		if(Es.Dacl.State & ACL_FLAG_PROTECTED)
			Sd.Control |= SE_DACL_PROTECTED;
		if(Es.Dacl.State & ACL_FLAG_AUTO_INHERIT)
			Sd.Control |= SE_DACL_AUTO_INHERITED | SE_DACL_AUTO_INHERIT_REQ;
		if(Es.Dacl.State & ACL_FLAG_UNTRUSTED)
			Sd.Control |= SE_DACL_UNTRUSTED;
	}

	if(Si & (SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION))
	{
		if(Es.Sacl.Acl || Es.Lacl.Acl)
		{
			auto_buffer Buff;
			ACL DestHeader;

			AclToNtAclStart(Buff, DestHeader);

			if(Es.Sacl.Acl && Si & SACL_SECURITY_INFORMATION)
				AclToNtAclAppend(Es.Sacl.Acl, DestHeader, Buff);
			
			if(Es.Lacl.Acl && Si & LABEL_SECURITY_INFORMATION)
				AclToNtAclAppend(Es.Lacl.Acl, DestHeader, Buff);
			
			auto_szpod<ACL> Acl = NtAclToAclEnd(Buff, DestHeader);

			Sd.Sacl = narrowing_cast<ULONG>(Ret.get_ct());
			Ret.append_szpod(Acl);
		}

		if((Es.Sacl.State | Es.Lacl.State) & ACL_FLAG_PRESENT)
			Sd.Control |= SE_SACL_PRESENT;
		if(Es.Sacl.State & ACL_FLAG_DEFAULTED)
			Sd.Control |= SE_SACL_DEFAULTED;
		if(Es.Sacl.State & ACL_FLAG_PROTECTED)
			Sd.Control |= SE_SACL_PROTECTED;
		if(Es.Sacl.State & ACL_FLAG_AUTO_INHERIT)
			Sd.Control |= SE_SACL_AUTO_INHERITED | SE_SACL_AUTO_INHERIT_REQ;
	}

	Sd.Control |= SE_SELF_RELATIVE;

	RtlCopyMemory(Ret, &Sd, sizeof Sd); //set header

	return auto_take_(c_cast<SECURITY_DESCRIPTOR_RELATIVE *>(Ret.release_unsafe()));
}

void NtSdToEs(IN SECURITY_DESCRIPTOR_RELATIVE *Sd, IN SECURITY_INFORMATION ReadSi, IN SECURITY_INFORMATION WriteSi, OUT SD_EDIT_STATE &Es, IN ULONG ExtraFlags = 0)
{
	Es.Dacl.Acl.reset();
	Es.Sacl.Acl.reset();
	Es.Owner.Sid.reset();
	Es.Group.Sid.reset();
	RtlZeroStruct(Es);
	Es.Version = Sd->Revision;
	Es.RMBits.Bits = Sd->Sbz1;

	if(Sd->Control & SE_DACL_PRESENT)
		Es.Dacl.State |= ACL_FLAG_PRESENT;
	if(!(WriteSi & DACL_SECURITY_INFORMATION))
		Es.Dacl.State |= ACL_FLAG_READONLY;

	if(ReadSi & (SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION))
	{
		if(Sd->Control & SE_SACL_PRESENT)
		{
			if(ReadSi & SACL_SECURITY_INFORMATION)
				Es.Sacl.State |= ACL_FLAG_PRESENT;

			if(!(WriteSi & SACL_SECURITY_INFORMATION))
				Es.Sacl.State |= ACL_FLAG_READONLY;

			if(ReadSi & LABEL_SECURITY_INFORMATION)
				Es.Lacl.State |= ACL_FLAG_PRESENT;

			if(!(WriteSi & LABEL_SECURITY_INFORMATION))
				Es.Lacl.State |= ACL_FLAG_READONLY;
		}

		if(Sd->Sacl)
		{
			ACL *Src = reinterpret_cast<ACL *>(reinterpret_cast<ULONG_PTR>(Sd) + Sd->Sacl);

			if(ReadSi & SACL_SECURITY_INFORMATION)
				Es.Sacl.Acl = NtAclToAcl(Src, &IsNonLabelAce);
			
			if(ReadSi & LABEL_SECURITY_INFORMATION)
				Es.Lacl.Acl = NtAclToAcl(Src, &IsLabelAce);
		}
		//else stay with default 0 for NULL
	}

	if(Sd->Owner)
		Es.Owner.State |= SID_FLAG_PRESENT;
	if(!(WriteSi & OWNER_SECURITY_INFORMATION))
		Es.Owner.State |= SID_FLAG_READONLY;

	if(Sd->Group)
		Es.Group.State |= SID_FLAG_PRESENT;
	if(!(WriteSi & GROUP_SECURITY_INFORMATION))
		Es.Group.State |= SID_FLAG_READONLY;

	if(Sd->Control & SE_OWNER_DEFAULTED)
		Es.Owner.State |= SID_FLAG_DEFAULTED;
	if(Sd->Control & SE_GROUP_DEFAULTED)
		Es.Group.State |= SID_FLAG_DEFAULTED;
	if(Sd->Control & SE_DACL_DEFAULTED)
		Es.Dacl.State |= ACL_FLAG_DEFAULTED;
	if(Sd->Control & SE_SACL_DEFAULTED)
		Es.Sacl.State |= ACL_FLAG_DEFAULTED;
	if(Sd->Control & SE_SACL_DEFAULTED)
		Es.Lacl.State |= ACL_FLAG_DEFAULTED;

	if(Sd->Control & SE_DACL_PROTECTED)
		Es.Dacl.State |= ACL_FLAG_PROTECTED;
	if(Sd->Control & SE_SACL_PROTECTED)
		Es.Sacl.State |= ACL_FLAG_PROTECTED;
	if(Sd->Control & SE_SACL_PROTECTED)
		Es.Lacl.State |= ACL_FLAG_PROTECTED;

	if(Sd->Control & SE_DACL_AUTO_INHERITED || Sd->Control & SE_DACL_AUTO_INHERIT_REQ)
		Es.Dacl.State |= ACL_FLAG_AUTO_INHERIT;

	if(Sd->Control & SE_SACL_AUTO_INHERITED || Sd->Control & SE_SACL_AUTO_INHERIT_REQ)
		Es.Sacl.State |= ACL_FLAG_AUTO_INHERIT;

	if(Sd->Control & SE_SACL_AUTO_INHERITED || Sd->Control & SE_SACL_AUTO_INHERIT_REQ)
		Es.Lacl.State |= ACL_FLAG_AUTO_INHERIT;

	Es.Sacl.State |= ACL_FLAG_AUDITFLAG_SUPPORT;
	Es.Lacl.State |= ACL_FLAG_LABEL_ACL;

	if(Sd->Control & SE_RM_CONTROL_VALID)
		Es.RMBits.State |= RMBITS_FLAG_PRESENT;

	Es.State |= ExtraFlags;

	if(Sd->Owner)
		Es.Owner.Sid = reinterpret_cast<SID *>(reinterpret_cast<ULONG_PTR>(Sd) + Sd->Owner);

	if(Sd->Group)
		Es.Group.Sid = reinterpret_cast<SID *>(reinterpret_cast<ULONG_PTR>(Sd) + Sd->Group);

	if(Sd->Dacl)
		Es.Dacl.Acl = NtAclToAcl(reinterpret_cast<ACL *>(reinterpret_cast<ULONG_PTR>(Sd) + Sd->Dacl));
}

}//namespace SDCon